#pragma once 

#include <iostream>
#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"

namespace RingBufferModule
{
    using namespace SemModule;
    using namespace MutexModule;

    template<typename T>
    class RingBuffer
    {
    public:
        RingBuffer(int capacity)
            :_ring(capacity),
            _capacity(capacity),
            _head(0),
            _tail(0),
            _datasem(0),
            _spacesem(capacity)
        {}
        void PushQueue(const T& in)
        {
            //生产者：申请完空间资源后新增数据资源
            _spacesem.P();

            //先申请信号量再申请锁资源，类似于先买票再排队，效率更高
            MutexGuard mutex(_producer_mutex);
            _ring[_tail] = in;
            _tail++;
            _tail %= _capacity;

            _datasem.V();
        }
        void PopQueue(T* out)
        {
            //消费者：申请完数据资源后释放空间资源
            _datasem.P();

            //先申请信号量再申请锁资源，类似于先买票再排队，效率更高
            MutexGuard mutexguard(_consumer_mutex);
            *out = _ring[_head];
            _head++;
            _head %= _capacity;

            _spacesem.V();
        }
        ~RingBuffer()
        {}
    private:
        std::vector<T> _ring;  //环形队列
        int _capacity;         //队列的总容量
        int _head;             //消费者从队头取数据的位置
        int _tail;             //生产者从队尾放数据的位置
        Sem _datasem;          //数据的信号量
        Sem _spacesem;         //空间的信号量

        //用于多生产多消费
        Mutex _producer_mutex; //生产者之间的互斥锁
        Mutex _consumer_mutex; //消费者之间的互斥锁
    };
}